/**
 * 
 */
package org.bankabeule.util;

import java.util.ArrayList;
import java.util.List;

import org.bankabeule.Account;
import org.bankabeule.Date;
import org.bankabeule.Model;
import org.bankabeule.Operation;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.util.EcoreUtil;

/**
 * @author sdudoit
 *
 */
public class Utils {

	
	/**
	 * Get account display 
	 */
	public static String getDisplay(Account account) {
		return account.getId()+" ("+account.getName()+")";
	}
	
	
	/**
	 * Get Date display 
	 */
	public static String getDisplay(Date date) {
		String month = date.getMonth() < 10 ? "-0" + date.getMonth() : "-"+date.getMonth();
		String day = date.getDay() < 10 ? "-0" + date.getDay() : "-"+date.getDay();
		return "" + date.getYear() + month + day;
	}
	
	
	/**
	 * Get value display 
	 */
	public static String getDisplay(double value) {
		return String.format("%.2f",value);
	}
	
	
	/**
	 * Compare two dates
	 */
	public static int compare(Date d1, Date d2) {
		int test = d1.getYear() - d2.getYear();
		if (test == 0) {
			test = d1.getMonth() - d2.getMonth();
			if (test == 0) {
				test = d1.getDay() - d2.getDay();
			}
		}
		return -test;
	}
	
	
	/**
	 * Merge two models
	 */
	public static void merge(Model srce, Model dest) throws Exception {
		for (Account srceAccount : srce.getAccounts()) {
			Account destAccount = findSimilar(srceAccount, dest.getAccounts());
			if (destAccount != null) {
				for (Operation srceOperation : srceAccount.getOperations()) {
					Operation destOperation = findSimilar(srceOperation, destAccount.getOperations());
					if (destOperation == null) {
						destAccount.getOperations().add((Operation) EcoreUtil.copy(srceOperation));
					}
				}
			} else {
				destAccount = (Account) EcoreUtil.copy(srceAccount);
				dest.getAccounts().add(destAccount);
			}
			calculateValue(destAccount);
		}
	}
	
	
	/**
	 * Try to find a similar account
	 */
	public static Account findSimilar(Account toFind, List<Account> list) {
		for (Account toTest : list) {
			if (toTest.getId().equals(toFind.getId())
					&& toTest.getBankid().equals(toFind.getBankid())
					&& toTest.getBranchid().equals(toFind.getBranchid())) {
				return toTest;
			}
		}
		return null;
	}
	
	
	
	
	/**
	 * Try to find a similar account
	 */
	public static Operation findSimilar(Operation toFind, List<Operation> list) {
		for (Operation toTest : list) {
			if (toTest.getId().equals(toFind.getId())
					&& (Utils.compare(toFind.getDate(), toTest.getDate()) == 0)
					&& (toFind.getValue() == toTest.getValue())) {
				return toTest;
			}
		}
		return null;
	}	
	
	
	/**
	 * Calculate value for account
	 */
	public static double calculateValue(Account account) {
		double value = account.getInitValue();
		for (Operation operation : account.getOperations()) {
			value = value + operation.getValue();
		}
		account.setValue(value);
		return value;
	}
	
	
	/**
	 * Calculate sum for operations list
	 */
	public static double calculateSum(List<Operation> operations) {
		double value = 0;
		for (Operation operation : operations) {
			value = value + operation.getValue();
		}
		return value;
	}	
	
	
	/**
	 * Find objects referencing given target
	 */
	public static List<EObject> findReferencers(EObject referencer, EObject reference) {
		List<EObject> results = new ArrayList<EObject>();		
		for (EObject ref : referencer.eCrossReferences()) {
			if (EcoreUtil.equals(ref, reference)) {
				if (!results.contains(referencer)) {
					results.add(referencer);
					break;
				}
			}
		}
		for (EObject child : referencer.eContents()) {
			results.addAll(findReferencers(child, reference));
		}
		return results;
	}
	
	
	/**
	 * Unset all referencers of given referenced object
	 */
	@SuppressWarnings("unchecked")
	public static void unsetReferencers(EObject referencer, EObject referenced) {
		List<EReference> references = referencer.eClass().getEAllReferences();
		for (EReference ref : references) {
			if (ref.getEReferenceType().equals(referenced.eClass())
					&& (!ref.isContainment())) {
				List<EObject> list = (List<EObject>) referencer.eGet(ref);
				list.remove(referenced);
			}
		}
		for (EObject child : referencer.eContents()) {
			unsetReferencers(child, referenced);
		}
	}
	
	
	/**
	 * Remove EObject
	 */
	public static void delete(EObject eObject) {
		unsetReferencers(EcoreUtil.getRootContainer(eObject, true), eObject);
		EcoreUtil.remove(eObject);
	}
	
	
}
